﻿using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.CommandLine;
using System.Reflection;
using System.Text.RegularExpressions;

namespace DemoCommandLine
{
    internal class Math
    {
        public Math(RootCommand rootCmd)
        {
            var expression = new Argument<string>(name: "expression");
            var subCom = new Command("math", "math expression") { expression };
            subCom.SetHandler((inputArgumentValue) =>
            {
                Handle(inputArgumentValue);
            }, expression);
            rootCmd.Add(subCom);
        }

        public static void Handle(string data)
        {
            CSharpCodeProvider provider = new CSharpCodeProvider();
            CompilerParameters parameters = new CompilerParameters()
            {
                GenerateInMemory = true
            };
            if (data == null)
            {
                return;
            }

            if (data.Contains("/"))
            {
                data = new Regex(@"\s+", RegexOptions.IgnoreCase).Replace(data, "", 1000);
                data = new Regex(@"[\d\.]+/", RegexOptions.IgnoreCase).Replace(data, "(double)$0", 1);
                data = new Regex(@"\s*[-\+\*\/]\s*", RegexOptions.IgnoreCase).Replace(data, " $0 ", 1000);
            }
            string code = string.Format(@"using System;
                public class Program
                {{
                    public double Main()
                    {{
                        return {0};
                    }}
                }}", data);
            CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
            if (results.Errors.HasErrors)
            {
                foreach (CompilerError error in results.Errors)
                {
                    Console.WriteLine(error.ErrorText);
                }
                return;
            }

            Type programType = results.CompiledAssembly.GetType("Program");
            object programInstance = Activator.CreateInstance(programType);
            MethodInfo mainMethod = programType.GetMethod("Main");
            object obj = mainMethod.Invoke(programInstance, null);
            double result = (double)obj;
            result = System.Math.Round(result, 3);
            Console.WriteLine($"{result}");
        }
    }
}
